home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / New System Software Extensions / QuickDraw™ GX v1.0ß2 / Interfaces & Libraries / graphics libraries / layout edit library.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-29  |  30.6 KB  |  1,245 lines  |  [TEXT/MPS ]

  1. /*
  2.   layout edit library.c
  3.   
  4.   Simple layout editing based on the TextEdit model.
  5.   
  6.   Copyright ®1992 Apple Computer, Inc. All rights reserved.
  7. */
  8.  
  9.   #include <Types.h>
  10.   #include <Memory.h>
  11.   #include <Events.h>
  12.   #include <OSUtils.h>
  13.   #include <Scrap.h>
  14.  
  15. #ifndef selectionLibraryIncludes
  16. #include "selection library.h"
  17. #endif
  18.  
  19. #ifndef layoutTypesIncludes
  20. #include "layout types.h"
  21. #endif
  22.  
  23. #ifndef layoutRoutinesIncludes
  24. #include "layout routines.h"
  25. #endif
  26.  
  27. #ifndef graphicsRoutinesIncludes
  28. #include "graphics routines.h"
  29. #endif
  30.  
  31. #include "graphics libraries.h"
  32.   #include "graphics toolbox.h"
  33. #include "storage library.h"
  34. #include "layout edit library.h"
  35.  
  36.  
  37. #define LockHandle(handle) (HLock ((Handle)handle), *handle)
  38. #define UnlockHandle(handle) HUnlock ((Handle)handle)
  39.  
  40. #define LockEditHandle(handle) (LayoutEditPtr) LockHandle(handle)
  41. #define UnlockEditHandle(handle) UnlockHandle(handle)
  42.  
  43. #define MIN(a,b) ((a) < (b)? (a): (b))
  44. #define MAX(a,b) ((a) > (b)? (a): (b))
  45.  
  46. #define maxRanges 10
  47. #define textBufferLength 16
  48. #define nextUpdateDelta 5
  49.  
  50. #define layoutOutOfDate 0x8000
  51. #define highlightOutOfDate 0x4000
  52.  
  53. enum
  54. { backSpace = 8,
  55.   leftArrow = 0x1C,
  56.   rightArrow,
  57.   upArrow,
  58.   downArrow
  59. };
  60.  
  61. typedef unsigned long ulong;
  62. typedef unsigned short ushort;
  63.  
  64. /*
  65.   The LayoutEditRecord contains the layout and highlight shapes,
  66.   the selection. flags contains flags which indicate that the
  67.   layout and highlight shapes haven't been rebuilt since their
  68.   corresponding data in the LayoutEditRecord has been changed.
  69.   
  70.   nextUpdate time, if non-zero tells the idle routine when to
  71.   update and re-draw the layout gxShape. 
  72.   
  73.   selectionRanges is working storage used for manipulating the
  74.   layout's selection. NOTE: the actual handle containing the
  75.   LayoutEditRecord will be large enough to contain the
  76.   SelectionOffsetRange array which immediately follows selectionRanges.
  77. */
  78. typedef struct {
  79.   short flags;
  80.   short highlightHideCount;
  81.   gxShape layout;
  82.   gxShape highlight;
  83.   SelectionHandle selection;
  84.   gxShape scrap;
  85.   ulong nextUpdateTime;
  86.   long deleteStartOffset;
  87.   long deleteEndOffset;
  88.   gxStyle insertionStyle;
  89.   short insertionLevel;
  90.   short textBufferOffset;
  91.   char textBuffer[textBufferLength];
  92.   SelectionRanges selectionRanges;
  93. } LayoutEditRecord, *LayoutEditPtr;
  94.  
  95.  
  96. /*
  97.   I N T E R N A L   R O U T I N E S
  98. */
  99.  
  100.  
  101. /*
  102.   Build a selection from start to end. If start and end are equal,
  103.   the selection will be a caret.
  104. */
  105. static void NewSelection(LayoutEditPtr layout, SelectionOffset start, SelectionOffset end)
  106. { layout->selectionRanges.rangeCount = 1;
  107.   layout->selectionRanges.ranges[0].minOffset = start;
  108.   layout->selectionRanges.ranges[0].maxOffset = end;
  109.   
  110.   if (layout->selection) DisposeSelection(layout->selection);
  111.   layout->selection = NewRangeSelection(&layout->selectionRanges.ranges[0]);
  112.   
  113.   layout->flags |= highlightOutOfDate;
  114. }
  115.  
  116. /*
  117.   Update the layout's highlight gxShape if the selection's
  118.   changed since we last built it.
  119. */
  120. static void UpdateHighlight(LayoutEditPtr layout)
  121. { if (layout->flags & highlightOutOfDate)
  122.   { DisposeShapeAt(&layout->highlight);
  123.     
  124.     layout->highlight = GetLayoutSelection(
  125.       layout->layout,
  126.       layout->selection,
  127.       0,
  128.       gxHighlightAverageAngle,
  129.       gxSplitCaretType);
  130.  
  131. #if 0   
  132.     SetShapeCommonTransfer(layout->highlight, gxXorMode);
  133. #else
  134.     SetShapeCommonTransfer(layout->highlight, gxHighlightMode);
  135.     SetShapeCommonColor(layout->highlight, gxWhite);
  136. #endif
  137.       
  138.     DisposeStyleAt(&layout->insertionStyle);
  139.     layout->insertionLevel = -1;
  140.  
  141.     layout->flags &= ~highlightOutOfDate;
  142.   }
  143. }
  144.  
  145. /*
  146.   Build a new selection from start to end, and a new highlight gxShape
  147.   that matches it.
  148. */
  149. static void NewSelectionAndHighlight(
  150.   LayoutEditPtr layout,
  151.   SelectionOffset start,
  152.   SelectionOffset end)
  153. { NewSelection(layout, start, end);
  154.   UpdateHighlight(layout);
  155. }
  156.  
  157. /*
  158.   Return the offset of the glyph before the one at the specified
  159.   offset in the layout's text.
  160. */
  161. static SelectionOffset GetPreviousOffset(gxShape layout, SelectionOffset offset)
  162. { ushort firstGlyph, secondGlyph;
  163.   gxLayoutOffsetState offsetState;
  164.   static SelectionOffset offsetStateSizes[] = {1, 1, 2, 2, 0};
  165.   
  166.   GXGetOffsetGlyphs(layout, offset, 0, &offsetState, &firstGlyph, &secondGlyph);
  167.   
  168.   return offset - offsetStateSizes[offsetState & ~gxOffsetInsideLigature];
  169. }
  170.  
  171. /*
  172.   Delete the text from the layout in the range specified by pRange.
  173. */
  174. static void DeleteRange(LayoutEditPtr layout, SelectionOffsetRange *pRange)
  175. { layout->deleteStartOffset = pRange->minOffset;
  176.   layout->deleteEndOffset = pRange->maxOffset;
  177.   layout->flags |= layoutOutOfDate;
  178. }
  179.  
  180.  
  181. /*
  182.   Delete the selected text from the layout. If the selection is a
  183.   caret, delete the character before the caret. Change the selection
  184.   to a caret just before the old selection.
  185. */
  186.  
  187. static void DeleteSelection(LayoutEditPtr layout)
  188. { SelectionType selectionType = GetSelectionType(layout->selection);
  189.   SelectionOffset newCaret;
  190.  
  191.   switch (selectionType)
  192.   { case emptySelection:
  193.       newCaret = 0;
  194.       break;
  195.     
  196.     case simpleCaret:
  197.     { SelectionOffset caret = GetCaretSelection(layout->selection, nil);
  198.     
  199.       if(layout->textBufferOffset)
  200.       { layout->textBufferOffset -= 1;
  201.         newCaret = caret - 1;
  202.       }
  203.       else if (caret > 0)
  204.       { SelectionOffsetRange range;
  205.         SelectionOffset previous = GetPreviousOffset(layout->layout, caret);
  206.       
  207.         range.minOffset = newCaret = previous;
  208.         range.maxOffset = caret;
  209.         DeleteRange(layout, &range);
  210.       }
  211.       else newCaret = caret;
  212.       
  213.     break;
  214.     }
  215.     
  216.     case simpleRange:
  217.     case discontiguousRange:
  218.     { long rangeCount;
  219.       SelectionRanges *ranges = &layout->selectionRanges;
  220.       SelectionOffsetRange *pRange;
  221.       
  222.       (void) GetRangeSelection(layout->selection, ranges);
  223.       rangeCount = ranges->rangeCount;
  224.       pRange = &ranges->ranges[rangeCount];
  225.       
  226.       /*
  227.         go through the ranges backwards to save sliding stuff
  228.         that'll get deleted later and so that the caret is set
  229.         for the first range.
  230.       */
  231.       while (--rangeCount >= 0)
  232.       { DeleteRange(layout, --pRange);
  233.         newCaret = pRange->minOffset;
  234.       }
  235.         
  236.       break;
  237.     }
  238.     
  239.   }
  240.   
  241.   /* set layout's selection to a caret before the deletion */
  242.   NewSelection(layout, newCaret, newCaret);
  243. }
  244.  
  245. /*
  246.   Make sure the handle is at least newSize bytes long;
  247.   If it isn't, grow it to be newSize + extra.
  248. */
  249. static Ptr GrowAndLockHandle(Handle handle, Size newSize, Size extra)
  250. { Size oldSize = GetHandleSize(handle);
  251.  
  252.   if (oldSize < newSize) SetHandleSize(handle, newSize + extra);
  253.   return LockHandle(handle);
  254. }
  255.  
  256. /*
  257.   Returns a gxRectangle covering the layout's bounds, and with
  258.   the layout's gxTransform.
  259. */
  260. static gxShape GetLayoutArea(gxShape layout)
  261. { gxRectangle bounds;
  262.   gxShape area;
  263.   
  264.   /* get the layout's bounds gxRectangle */
  265.   GXGetShapeTypographicBounds(layout, &bounds);
  266.   area = GXNewRectangle(&bounds);
  267.   
  268.   /* apply the layout's gxTransform to it */
  269. #ifdef debugging
  270.   GXIgnoreGraphicsNotice(transform_already_set);
  271. #endif
  272.   GXSetShapeTransform(area, GXGetShapeTransform(layout));
  273. #ifdef debugging
  274.   GXPopGraphicsNotice();
  275. #endif  
  276.   return area;
  277. }
  278.  
  279. static gxShape GetEraser(gxShape layout)
  280. { gxShape eraser = GetLayoutArea(layout);
  281.  
  282.   GXInsetShape(eraser, -5 * fixed1);
  283.   SetShapeCommonColor(eraser, gxWhite);
  284.   
  285.   return eraser;
  286. }
  287.  
  288. /*
  289.   If the backing store in the LayoutEditRecord has changed since
  290.   the layout gxShape was last built, rebuild the layout.
  291. */
  292. static void UpdateLayout(LayoutEditPtr layout)
  293. { if (layout->flags & layoutOutOfDate)
  294.   { gxShape eraseOldLayout = GetEraser(layout->layout);
  295.     void *textBuffer = &layout->textBuffer, **text = nil;
  296.     short *levels = nil, levelRunCount = 0, styleRunCount = 0, textRunCount = 0,
  297.           *textRunLength = nil, *styleRunLength = nil, *levelRunLength = nil;
  298.     gxStyle *styles = nil;
  299.     
  300.     if (layout->textBufferOffset)
  301.     { text = &textBuffer;
  302.       textRunLength = &layout->textBufferOffset;
  303.       textRunCount = 1;
  304.       
  305.       if (layout->insertionStyle)
  306.       { styles = &layout->insertionStyle;
  307.         styleRunLength = textRunLength;
  308.         styleRunCount = 1;
  309.       }
  310.       
  311.       if (layout->insertionLevel >= 0)
  312.       { levels = &layout->insertionLevel;
  313.         levelRunLength = textRunLength;
  314.         levelRunCount = 1;
  315.       }
  316.     }
  317.     
  318.     /* edit the layout */ 
  319.     GXSetLayoutParts(
  320.       layout->layout,
  321.       layout->deleteStartOffset,
  322.       layout->deleteEndOffset,
  323.       textRunCount,
  324.       textRunLength,
  325.       (void *) text,
  326.       styleRunCount,
  327.       styleRunLength,
  328.       styles,
  329.       levelRunCount,
  330.       levelRunLength,
  331.       levels);
  332.     
  333.     /* erase the old layout and draw the new one */
  334.     GXDrawShape(eraseOldLayout);
  335.     GXDrawShape(layout->layout);
  336.  
  337.     layout->flags &= ~layoutOutOfDate;
  338.     layout->nextUpdateTime = 0;
  339.     layout->textBufferOffset = 0;
  340.     layout->deleteStartOffset = -1;
  341.     layout->insertionLevel = -1;
  342.     DisposeStyleAt(&layout->insertionStyle);
  343.     
  344.     GXDisposeShape(eraseOldLayout);
  345.   }
  346. }
  347.  
  348. /*
  349.   Add a character to the layout at the selection. This routine
  350.   assumes that the selection is a caret; i.e. if it was a range,
  351.   the range has been deleted leaving a caret.
  352. */
  353. static void InsertByte(LayoutEditPtr layout, char byte)
  354. { SelectionOffset caret = layout->selectionRanges.ranges[0].minOffset;
  355.   
  356.   if (layout->textBufferOffset >= textBufferLength) UpdateLayout(layout);
  357.   
  358.   if (layout->deleteStartOffset < 0)
  359.     layout->deleteStartOffset = layout->deleteEndOffset = caret;
  360.     
  361.   layout->textBuffer[layout->textBufferOffset++] = byte;
  362.   
  363.   /* set selection to a caret after the new byte */
  364.   NewSelection(layout, caret + 1, caret + 1);
  365.   
  366.   layout->flags |= layoutOutOfDate;
  367. }
  368.  
  369. /*
  370.   If the highlight gxShape isn't already hidden, erase
  371.   it by drawing it on top of itself.
  372. */
  373. static void HideHighlight(LayoutEditPtr layout)
  374. { if (layout->highlightHideCount++ == 0)
  375.     GXDrawShape(layout->highlight);
  376. }
  377.  
  378. /*
  379.   If the highlight gxShape isn't already visible, update it
  380.   and draw it.
  381. */
  382. static void ShowHighlight(LayoutEditPtr layout)
  383. { if (--layout->highlightHideCount <= 0)
  384.   { UpdateHighlight(layout);
  385.     GXDrawShape(layout->highlight);
  386.     layout->highlightHideCount = 0;
  387.   }
  388. }
  389.  
  390. static void DrawChangedLayout(LayoutEditPtr layout, gxShape eraser)
  391. { HideHighlight(layout);
  392.   GXDrawShape(eraser);
  393.   GXDrawShape(layout->layout);
  394.   layout->flags |= highlightOutOfDate;
  395.   ShowHighlight(layout);
  396. }
  397.  
  398. static void CopySelection(LayoutEditPtr layout)
  399. { SelectionType selectionType = GetSelectionType(layout->selection);
  400.  
  401.   switch (selectionType)
  402.   { case emptySelection:
  403.     case simpleCaret:
  404.     case discontiguousRange:
  405.       break;
  406.     
  407.     case simpleRange:
  408.     { SelectionRanges *ranges = &layout->selectionRanges;
  409.       SelectionOffsetRange *pRange;
  410.       
  411.       (void) GetRangeSelection(layout->selection, ranges);
  412.       pRange = ranges->ranges;
  413.  
  414.     #if 1     
  415.       layout->scrap = GXGetLayoutShapeParts(layout->layout, pRange->minOffset, pRange->maxOffset, layout->scrap);       
  416.     #else
  417.       layout->scrap = GXGetShapeParts(layout->layout, pRange->minOffset + 1, pRange->maxOffset - pRange>minOffset, layout->scrap);
  418.     #endif
  419.  
  420.       break;
  421.     }
  422.   }
  423. }
  424.  
  425. static void PasteSelection(LayoutEditPtr layout)
  426. { SelectionType selectionType = GetSelectionType(layout->selection);
  427.   SelectionOffset newCaret, startOffset, endOffset;
  428.   gxShape eraser;
  429.  
  430.   switch (selectionType)
  431.   { case emptySelection:
  432.     case discontiguousRange:
  433.       return;
  434.   
  435.     case simpleCaret:
  436.       newCaret = startOffset = endOffset = GetCaretSelection(layout->selection, nil);
  437.       break;
  438.     
  439.     case simpleRange:
  440.     { SelectionRanges *ranges = &layout->selectionRanges;
  441.       
  442.       (void) GetRangeSelection(layout->selection, ranges);
  443.       startOffset = ranges->ranges[0].minOffset;
  444.       endOffset = ranges->ranges[0].maxOffset;
  445.       newCaret = startOffset;
  446.       
  447.       
  448.       break;
  449.     }
  450.  
  451.   }
  452.   eraser = GetEraser(layout->layout);
  453.   newCaret += GXGetLayout(layout->scrap, nil, 0, nil, nil, 0, nil, nil, nil, nil);
  454. #if 1
  455.   GXSetLayoutShapeParts(layout->layout, startOffset, endOffset, layout->scrap);
  456. #else
  457.   GXSetShapeParts(layout->layout, startOffset + 1, endOffset - startOffset, layout->scrap, 0);
  458. #endif
  459.   GXDrawShape(eraser);
  460.   GXDrawShape(layout->layout);
  461.   NewSelection(layout, newCaret, newCaret);
  462.  
  463.   GXDisposeShape(eraser);
  464. }
  465.  
  466. static void AdjustSelectedLevels(LayoutEditPtr layout, short levelAdjust)
  467. { short *lengths, *levels;
  468.   long levelRunCount;
  469.   SelectionType selectionType = GetSelectionType(layout->selection);
  470.   SelectionRanges *ranges = &layout->selectionRanges;
  471.   SelectionOffset startOffset, endOffset;
  472.   
  473.   UpdateLayout(layout);
  474.   
  475.   switch (selectionType)
  476.   { case emptySelection:
  477.     case discontiguousRange:
  478.       return;
  479.       
  480.     case simpleCaret:
  481.       startOffset = endOffset = GetCaretSelection(layout->selection, nil);
  482.       break;
  483.     
  484.     case simpleRange:
  485.     { SelectionRanges *ranges = &layout->selectionRanges;
  486.     
  487.       GetRangeSelection(layout->selection, ranges);
  488.       startOffset = ranges->ranges[0].minOffset;
  489.       endOffset = ranges->ranges[0].maxOffset;
  490.     }
  491.     break;
  492.   }
  493.   
  494.   GXGetLayoutParts(layout->layout, startOffset, endOffset, nil, nil, nil, nil, &levelRunCount, nil, nil);
  495.   
  496.   levels = (short *) NewPtr(levelRunCount * sizeof(short));
  497.   lengths = (short *) NewPtr(levelRunCount * sizeof(short));
  498.   
  499.   GXGetLayoutParts(layout->layout, startOffset, endOffset, nil, nil, nil, nil, nil, lengths, levels);
  500.   
  501.   if (startOffset == endOffset) layout->insertionLevel = MAX(*levels + levelAdjust, 0);
  502.   else
  503.   { gxShape eraser = GetEraser(layout->layout);
  504.     short i, length = endOffset - startOffset, *pl;
  505.   
  506.     for (pl = levels, i = levelRunCount - 1; i >= 0; --i, ++pl)
  507.       *pl = MAX(*pl + levelAdjust, 0);
  508.  
  509.     
  510.     GXSetLayoutParts(
  511.       layout->layout,
  512.       startOffset,
  513.       endOffset,
  514.       0,
  515.       nil,
  516.       nil,
  517.       0,
  518.       nil,
  519.       nil,
  520.       levelRunCount,
  521.       lengths,
  522.       levels);
  523.       
  524.     HideHighlight(layout);
  525.     GXDrawShape(eraser);
  526.     GXDrawShape(layout->layout);
  527.     ShowHighlight(layout);
  528.     GXDisposeShape(eraser);
  529.     
  530.     layout->insertionLevel = -1;
  531.   }
  532.   
  533.   DisposPtr((Ptr) lengths);
  534.   DisposPtr((Ptr) levels);
  535. }
  536.  
  537.  
  538. /*
  539.   P U B L I C   R O U T I N E S
  540. */
  541.  
  542. LayoutEditHandle LayoutEditHandleFromLayout(gxShape layoutShape)
  543. { LayoutEditHandle handle;
  544.   LayoutEditPtr layout;
  545.  
  546.   handle = (LayoutEditHandle) NewHandle(
  547.     sizeof(LayoutEditRecord) + maxRanges * sizeof(SelectionOffsetRange));
  548.   
  549.   layout = LockEditHandle(handle);
  550.   layout->layout = layoutShape;
  551.   layout->flags = 0;
  552.   layout->selection = nil;
  553.   layout->highlight = nil;
  554.   layout->scrap = nil;
  555.   layout->highlightHideCount = 0;
  556.   layout->nextUpdateTime = 0;
  557.   layout->textBufferOffset = 0;
  558.   layout->insertionStyle = nil;
  559.   layout->insertionLevel = -1;
  560.   layout->deleteStartOffset = -1;
  561.   
  562.   /* set the initial selection to a caret before the first character */
  563.   NewSelectionAndHighlight(layout, 0, 0);
  564.     
  565.   UnlockHandle(handle);
  566.   
  567.   return handle;
  568. }
  569.  
  570. LayoutEditHandle NewLayoutEditHandle(
  571.   long textRunCount,
  572.   const short textRunLengths[],
  573.   const void *text[],
  574.   long styleRunCount,
  575.   const short styleRunLengths[],
  576.   const gxStyle styles[],
  577.   long levelRunCount,
  578.   const short levelRunLengths[],
  579.   const short levels[],
  580.   gxLayoutOptions *layoutOptions,
  581.   gxPoint *position,
  582.   gxStyle defaultStyle)
  583. { gxShape layoutShape;
  584.   
  585.   /* just call GXNewLayout for param error checking */
  586.   layoutShape = GXNewLayout(
  587.     textRunCount,
  588.     textRunLengths,
  589.     text,
  590.     styleRunCount,
  591.     styleRunLengths,
  592.     styles,
  593.     levelRunCount,
  594.     levelRunLengths,
  595.     levels,
  596.     layoutOptions,
  597.     position);
  598.   
  599.   /* If GXNewLayout posts an error, we won't get here... */
  600.  
  601.   if (defaultStyle) GXSetShapeStyle(layoutShape, defaultStyle);
  602.   return LayoutEditHandleFromLayout(layoutShape);
  603. }
  604.  
  605. long GetLayoutEditHandle(
  606.   LayoutEditHandle handle,
  607.   void *text,
  608.   long *styleRunCount,
  609.   short styleRunLengths[],
  610.   gxStyle styles[],
  611.   long *levelRunCount,
  612.   short levelRunLengths[],
  613.   short levels[],
  614.   gxLayoutOptions *layoutOptions,
  615.   gxPoint *position)
  616. { LayoutEditPtr layout = LockEditHandle(handle);
  617.   long result;
  618.   
  619.   UpdateLayout(layout);
  620.   
  621.   result = GXGetLayout(
  622.     layout->layout,
  623.     text,
  624.     styleRunCount,
  625.     styleRunLengths,
  626.     styles,
  627.     levelRunCount,
  628.     levelRunLengths,
  629.     levels,
  630.     layoutOptions,
  631.     position);
  632.   
  633.   UnlockHandle(handle);
  634.   
  635.   return result;
  636. }
  637.  
  638. void SetLayoutEditHandle(
  639.   LayoutEditHandle handle,
  640.   long textRunCount,
  641.   const short textRunLengths[],
  642.   const void *text[],
  643.   long styleRunCount,
  644.   const short styleRunLengths[],
  645.   const gxStyle styles[],
  646.   long levelRunCount,
  647.   const short levelRunLengths[],
  648.   const short levels[],
  649.   const gxLayoutOptions *layoutOptions,
  650.   const gxPoint *position)
  651. { LayoutEditPtr layout = LockEditHandle(handle);
  652.   gxShape eraser;
  653.  
  654.   UpdateLayout(layout);
  655.   eraser = GetEraser(layout->layout);
  656.   
  657.   GXSetLayout(
  658.     layout->layout,
  659.     textRunCount,
  660.     textRunLengths,
  661.     text,
  662.     styleRunCount,
  663.     styleRunLengths,
  664.     styles,
  665.     levelRunCount,
  666.     levelRunLengths,
  667.     levels,
  668.     layoutOptions,
  669.     position);
  670.   
  671.   DrawChangedLayout(layout, eraser);
  672.   
  673.   GXDisposeShape(eraser);
  674.   UnlockHandle(handle);
  675. }
  676.  
  677. void SetLayoutEditHandleParts(
  678.   LayoutEditHandle handle,
  679.   gxByteOffset oldStartOffset,
  680.   gxByteOffset oldEndOffset,
  681.   long newTextRunCount,
  682.   const short newTextRunLengths[],
  683.   const void *newText[],
  684.   long newStyleRunCount,
  685.   const short newStyleRunLengths[],
  686.   const gxStyle newStyles[],
  687.   long newLevelRunCount,
  688.   const short newLevelRunLengths[],
  689.   const short newLevels[])
  690. { LayoutEditPtr layout = LockEditHandle(handle);
  691.   gxShape eraser;
  692.  
  693.   UpdateLayout(layout);
  694.   eraser = GetEraser(layout->layout);
  695.   
  696.   GXSetLayoutParts(
  697.     layout->layout,
  698.     oldStartOffset,
  699.     oldEndOffset,
  700.     newTextRunCount,
  701.     newTextRunLengths,
  702.     newText,
  703.     newStyleRunCount,
  704.     newStyleRunLengths,
  705.     newStyles,
  706.     newLevelRunCount,
  707.     newLevelRunLengths,
  708.     newLevels);
  709.   
  710.   DrawChangedLayout(layout, eraser);
  711.   
  712.   GXDisposeShape(eraser);
  713.   UnlockHandle(handle);
  714. }
  715.  
  716. void SetLayoutEditHandleShapeParts(
  717.   LayoutEditHandle handle,
  718.   gxByteOffset startOffset,
  719.   gxByteOffset endOffset,
  720.   gxShape insert)
  721. { LayoutEditPtr layout = LockEditHandle(handle);
  722.   gxShape eraser;
  723.  
  724.   UpdateLayout(layout);
  725.   eraser = GetEraser(layout->layout);
  726.   
  727.   GXSetLayoutShapeParts(
  728.     layout->layout,
  729.     startOffset,
  730.     endOffset,
  731.     insert);
  732.   
  733.   DrawChangedLayout(layout, eraser);
  734.   
  735.   GXDisposeShape(eraser);
  736.   UnlockHandle(handle);
  737. }
  738.  
  739. long GetLayoutEditHandleParts(
  740.   LayoutEditHandle handle,
  741.   gxByteOffset startOffset,
  742.   gxByteOffset endOffset,
  743.   void *text,
  744.   long *styleRunCount,
  745.   short styleRunLengths[],
  746.   gxStyle styles[],
  747.   long *levelRunCount,
  748.   short levelRunLengths[],
  749.   short levels[])
  750. { LayoutEditPtr layout = LockEditHandle(handle);
  751.   long result;
  752.   
  753.   UpdateLayout(layout);
  754.   
  755.   result = GXGetLayoutParts(
  756.     layout->layout,
  757.     startOffset,
  758.     endOffset,
  759.     text,
  760.     styleRunCount,
  761.     styleRunLengths,
  762.     styles,
  763.     levelRunCount,
  764.     levelRunLengths,
  765.     levels);
  766.   
  767.   UnlockHandle(handle);
  768.   return result;
  769. }
  770.  
  771. gxShape GetLayoutEditHandleShapeParts(
  772.   LayoutEditHandle handle,
  773.   gxByteOffset startOffset,
  774.   gxByteOffset endOffset,
  775.   gxShape dest)
  776. { LayoutEditPtr layout = LockEditHandle(handle);
  777.   gxShape result;
  778.   
  779.   UpdateLayout(layout);
  780.   
  781.   result = GXGetLayoutShapeParts(layout->layout, startOffset, endOffset, dest);
  782.   
  783.   UnlockHandle(handle);
  784.   return result;
  785. }
  786.  
  787. void LayoutEditIdle(LayoutEditHandle handle)
  788. { ulong ticks = TickCount();
  789.   ulong next = ((LayoutEditPtr) *handle)->nextUpdateTime;
  790.   
  791.   if (next && ticks >= next)
  792.   { LayoutEditPtr layout = LockEditHandle(handle);
  793.     
  794.     HideHighlight(layout);
  795.     UpdateLayout(layout);
  796.     ShowHighlight(layout);
  797.     
  798.     UnlockEditHandle(handle);
  799.   }
  800. }
  801.  
  802.  
  803. void LayoutEditClick(LayoutEditHandle handle, gxPoint hitDown)
  804. { LayoutEditPtr layout = LockEditHandle(handle); 
  805.   gxPoint lastPoint = hitDown;
  806.   SelectionOffset firstHitOffset, lastHitOffset;
  807.   gxLayoutHitInfo hitInfo;
  808.   boolean oldIsCaret, newIsCaret = true;
  809.   gxShape diffHighlight = nil, oldHighlight = nil;
  810.   gxViewPort layoutViewPort = GetShapeViewPort(layout->layout);
  811.  
  812.   /* get the offset for the hit down gxPoint */
  813.   GXHitTestLayout(layout->layout, &hitDown, gxHighlightAverageAngle, &hitInfo, nil);
  814.   firstHitOffset = lastHitOffset = (SelectionOffset) hitInfo.hitSideOffset;
  815.   
  816.   /* erase the old highlight */
  817.   GXDrawShape(layout->highlight);
  818.  
  819.   /* Make the selection a caret at firstHitOffset */
  820.   NewSelectionAndHighlight(layout, firstHitOffset, firstHitOffset);
  821.   GXDrawShape(layout->highlight);
  822.  
  823.   /* recompute the selection and highlight while the mouse button is still down */
  824.   while (Button())
  825.   { GXGetViewPortMouse(layoutViewPort, &hitDown);
  826.   
  827.     /* continue if the mouse hasn't moved */
  828.     if (hitDown.x == lastPoint.x && hitDown.y == lastPoint.y) continue;
  829.     
  830.     lastPoint = hitDown;
  831.     GXHitTestLayout(layout->layout, &hitDown, gxHighlightAverageAngle, &hitInfo, nil);
  832.     
  833.     /* continue if the selection hasn't changed */
  834.     if (hitInfo.hitSideOffset == lastHitOffset) continue;
  835.     
  836.     oldIsCaret = newIsCaret;
  837.  
  838.     /* save the old highlight and calculate the new one */
  839.     lastHitOffset = (SelectionOffset) hitInfo.hitSideOffset;
  840.     newIsCaret = (lastHitOffset == firstHitOffset);
  841.     
  842.     if (oldIsCaret != newIsCaret)
  843.     { GXDrawShape(layout->highlight);
  844.       NewSelectionAndHighlight(layout, firstHitOffset, lastHitOffset);
  845.       GXDrawShape(layout->highlight);
  846.     }
  847.     else
  848.     { oldHighlight = GXCopyToShape(oldHighlight, layout->highlight);
  849.       NewSelectionAndHighlight(layout, firstHitOffset, lastHitOffset);
  850.     
  851.       /* to reduce flicker, draw the difference between the new and old highlight */
  852.       diffHighlight = GXCopyToShape(diffHighlight, layout->highlight);
  853.       GXExcludeShape(diffHighlight, oldHighlight);
  854.       GXDrawShape(diffHighlight);
  855.     }
  856.   }
  857.  
  858.   DisposeShapeAt(&diffHighlight);
  859.   DisposeShapeAt(&oldHighlight);
  860. }
  861.  
  862.  
  863. void LayoutEditActivate(LayoutEditHandle handle)
  864. { LayoutEditPtr layout = LockEditHandle(handle);
  865.  
  866.   UpdateLayout(layout);
  867.   ShowHighlight(layout);
  868.   UnlockEditHandle(handle);
  869. }
  870.  
  871.  
  872. void LayoutEditDeactivate(LayoutEditHandle handle)
  873. { LayoutEditPtr layout = LockEditHandle(handle);
  874.  
  875.   HideHighlight(layout);
  876.   UnlockEditHandle(handle);
  877. }
  878.  
  879.  
  880. void LayoutEditKey(LayoutEditHandle handle, char key)
  881. { LayoutEditPtr layout = LockEditHandle(handle);
  882.  
  883.   switch (key)
  884.   { case leftArrow:
  885.     case rightArrow:
  886.     { SelectionOffset newCaret; 
  887.     
  888.       HideHighlight(layout);
  889.       UpdateLayout(layout);
  890.       
  891.       if (key == leftArrow)
  892.         newCaret = GXGetLeftVisualOffset(
  893.           layout->layout,
  894.           layout->selectionRanges.ranges[0].minOffset);
  895.       else newCaret = GXGetRightVisualOffset(
  896.         layout->layout,
  897.         layout->selectionRanges.ranges[0].maxOffset);
  898.       
  899.       NewSelection(layout, newCaret, newCaret);
  900.       ShowHighlight(layout);
  901.       break;
  902.     }
  903.     
  904.     case backSpace:
  905.     {
  906.       DeleteSelection(layout);
  907.       if (layout->nextUpdateTime == 0)
  908.         layout->nextUpdateTime = TickCount() + nextUpdateDelta;
  909.       break;
  910.     }
  911.     
  912.     default:
  913.     { 
  914.     
  915.       switch (GetSelectionType(layout->selection))
  916.       { case emptySelection:
  917.           break;
  918.         
  919.         case discontiguousRange:
  920.         case simpleRange:
  921.           DeleteSelection(layout);
  922.         
  923.         case simpleCaret:
  924.           InsertByte(layout, key);
  925.           if (layout->nextUpdateTime == 0)
  926.             layout->nextUpdateTime = TickCount() + nextUpdateDelta;
  927.       }
  928.     }
  929.   }
  930.   
  931.   UnlockEditHandle(handle);
  932. }
  933.  
  934.  
  935. void LayoutEditUpdate(LayoutEditHandle handle)
  936. { LayoutEditPtr layout = LockEditHandle(handle);
  937.  
  938.   GXDrawShape(layout->layout);
  939.   ShowHighlight(layout);
  940.   
  941.   UnlockEditHandle(handle);
  942. }
  943.  
  944.  
  945. SelectionHandle LayoutEditGetSelection(LayoutEditHandle handle)
  946. {  LayoutEditPtr layout = (LayoutEditPtr) LockHandle(handle);
  947.    SelectionHandle selection;
  948.  
  949.    UpdateLayout(layout);
  950.    selection = layout->selection;
  951.    UnlockEditHandle(handle);
  952.  
  953.    return selection;
  954. }
  955.  
  956. void LayoutEditSetSelection(LayoutEditHandle handle, SelectionHandle selection)
  957. { LayoutEditPtr layout = LockEditHandle(handle);
  958.   SelectionOffset end, start;
  959.  
  960.   UpdateLayout(layout);
  961.   
  962.   switch (GetSelectionType(selection))
  963.   { case emptySelection:
  964.     case discontiguousRange:
  965.       UnlockEditHandle(handle);
  966.       return;
  967.       
  968.     case simpleCaret:
  969.       start = end = GetCaretSelection(selection, nil);
  970.       break;
  971.     
  972.     case simpleRange:
  973.     { SelectionRanges *ranges = &layout->selectionRanges;
  974.     
  975.       (void) GetRangeSelection(layout->selection, ranges);
  976.       start = ranges->ranges[0].minOffset;
  977.       end = ranges->ranges[0].maxOffset;
  978.       break;
  979.     }
  980.   }
  981.   
  982.   HideHighlight(layout);
  983.   NewSelection(layout, start, end);
  984.   ShowHighlight(layout);
  985.   
  986.   UnlockEditHandle(handle);
  987. }
  988.  
  989. void LayoutEditSetStyle(LayoutEditHandle handle, gxStyle newStyle)
  990. { LayoutEditPtr layout = LockEditHandle(handle);
  991.   SelectionType selectionType = GetSelectionType(layout->selection);
  992.   
  993.   UpdateLayout(layout);
  994.   
  995.   switch (selectionType)
  996.   { case emptySelection:
  997.     case discontiguousRange:
  998.       /* should never happen */
  999.       break;
  1000.       
  1001.     case simpleCaret:
  1002.       layout->insertionStyle = GXCloneStyle(newStyle);
  1003.       break;
  1004.       
  1005.     case simpleRange:
  1006.     { SelectionRanges *ranges = &layout->selectionRanges;
  1007.       gxShape eraser = GetEraser(layout->layout);
  1008.       SelectionOffset startOffset, endOffset;
  1009.       short length;
  1010.     
  1011.       (void) GetRangeSelection(layout->selection, ranges);
  1012.       
  1013.       startOffset = ranges->ranges[0].minOffset;
  1014.       endOffset = ranges->ranges[0].maxOffset;
  1015.       length = endOffset - startOffset;
  1016.       
  1017.       GXSetLayoutParts(
  1018.         layout->layout,
  1019.         startOffset,
  1020.         endOffset,
  1021.         0,
  1022.         nil,
  1023.         nil,
  1024.         1,
  1025.         &length,
  1026.         &newStyle,
  1027.         0,
  1028.         nil,
  1029.         nil);
  1030.         
  1031.       DrawChangedLayout(layout, eraser);
  1032.       GXDisposeShape(eraser);
  1033.       
  1034.       layout->insertionStyle = nil;
  1035.       break;
  1036.     }
  1037.   }
  1038.  
  1039.   UnlockEditHandle(handle);
  1040. }
  1041.  
  1042. void LayoutEditIncrementLevel(LayoutEditHandle handle)
  1043. { LayoutEditPtr layout = LockEditHandle(handle);
  1044.  
  1045.   HideHighlight(layout);
  1046.   AdjustSelectedLevels(layout, 1);
  1047.   layout->flags |= highlightOutOfDate;
  1048.   ShowHighlight(layout);
  1049.   
  1050.   UnlockEditHandle(handle);
  1051. }
  1052.  
  1053. void LayoutEditDecrementLevel(LayoutEditHandle handle)
  1054. { LayoutEditPtr layout = LockEditHandle(handle);
  1055.  
  1056.   HideHighlight(layout);
  1057.   AdjustSelectedLevels(layout, -1);
  1058.   layout->flags |= highlightOutOfDate;
  1059.   ShowHighlight(layout);
  1060.   
  1061.   UnlockEditHandle(handle);
  1062. }
  1063.  
  1064. void LayoutEditSetLevel(LayoutEditHandle handle, long level)
  1065. { LayoutEditPtr layout = LockEditHandle(handle);
  1066.   SelectionType selectionType = GetSelectionType(layout->selection);
  1067.   short newLevel = level;
  1068.   
  1069.   UpdateLayout(layout);
  1070.   
  1071.   switch (selectionType)
  1072.   { case emptySelection:
  1073.     case discontiguousRange:
  1074.       /* should never happen */
  1075.       break;
  1076.       
  1077.     case simpleCaret:
  1078.       layout->insertionLevel = newLevel;
  1079.       break;
  1080.       
  1081.     case simpleRange:
  1082.     { SelectionRanges *ranges = &layout->selectionRanges;
  1083.       gxShape eraser = GetEraser(layout->layout);
  1084.       SelectionOffset startOffset, endOffset;
  1085.       short length;
  1086.     
  1087.       (void) GetRangeSelection(layout->selection, ranges);
  1088.       
  1089.       startOffset = ranges->ranges[0].minOffset;
  1090.       endOffset = ranges->ranges[0].maxOffset;
  1091.       length = endOffset - startOffset;
  1092.       
  1093.       GXSetLayoutParts(
  1094.         layout->layout,
  1095.         startOffset,
  1096.         endOffset,
  1097.         0,
  1098.         nil,
  1099.         nil,
  1100.         0,
  1101.         nil,
  1102.         nil,
  1103.         1,
  1104.         &length,
  1105.         &newLevel);
  1106.         
  1107.       HideHighlight(layout);
  1108.       GXDrawShape(eraser);
  1109.       GXDrawShape(layout->layout);
  1110.       layout->flags |= highlightOutOfDate;
  1111.       ShowHighlight(layout);
  1112.       GXDisposeShape(eraser);
  1113.       
  1114.       layout->insertionLevel = -1;
  1115.       break;
  1116.     }
  1117.   }
  1118.  
  1119.   UnlockEditHandle(handle);
  1120. }
  1121.  
  1122. void LayoutEditCut(LayoutEditHandle handle)
  1123. { LayoutEditPtr layout = LockEditHandle(handle);
  1124.   SelectionType selectionType = GetSelectionType(layout->selection);
  1125.  
  1126.   if (selectionType != emptySelection && selectionType != simpleCaret)
  1127.   { CopySelection(layout);
  1128.     DeleteSelection(layout);
  1129.     HideHighlight(layout);
  1130.     UpdateLayout(layout);
  1131.     ShowHighlight(layout);
  1132.   }
  1133.   
  1134.   UnlockEditHandle(handle);
  1135. }
  1136.  
  1137. void LayoutEditCopy(LayoutEditHandle handle)
  1138. { LayoutEditPtr layout = LockEditHandle(handle);
  1139.  
  1140.   CopySelection(layout);
  1141.   
  1142.   UnlockEditHandle(handle);
  1143. }
  1144.  
  1145. void LayoutEditPaste(LayoutEditHandle handle)
  1146. { LayoutEditPtr layout = LockEditHandle(handle);
  1147.  
  1148.   HideHighlight(layout);
  1149.   PasteSelection(layout);
  1150.   ShowHighlight(layout);
  1151.   
  1152.   UnlockEditHandle(handle);
  1153. }
  1154.  
  1155. void LayoutEditClear(LayoutEditHandle handle)
  1156. { LayoutEditPtr layout = LockEditHandle(handle);
  1157.  
  1158.   DeleteSelection(layout);
  1159.   HideHighlight(layout);
  1160.   UpdateLayout(layout);
  1161.   ShowHighlight(layout);
  1162.   
  1163.   UnlockEditHandle(handle);
  1164. }
  1165.  
  1166. void LayoutEditFromScrap(LayoutEditHandle handle)
  1167. { LayoutEditPtr layout = LockEditHandle(handle);
  1168.   long offset, length;
  1169.   Handle buffer = NewHandle(0);
  1170.   
  1171.   if (GetScrap(buffer, 'FLAY', &offset) > 0)
  1172.   { long portCount = GXGetShapeViewPorts(GXGetDefaultShape(gxLayoutType), nil);
  1173.     gxViewPort *ports = (gxViewPort *) NewPtr(portCount * sizeof(gxViewPort));
  1174.   
  1175.     GXGetShapeViewPorts(GXGetDefaultShape(gxLayoutType), ports);
  1176.     DisposeShapeAt(&layout->scrap);
  1177.   
  1178.     layout->scrap = HandleToShape(buffer, portCount, ports);
  1179.     DisposePtr((Ptr) ports);
  1180.   }
  1181.   else if ((length = GetScrap(buffer, 'TEXT', &offset)) > 0)
  1182.   { void *text = (void *) LockHandle(buffer);
  1183.     short runLength = length;
  1184.     gxStyle defaultStyle = GXGetShapeStyle(layout->layout);
  1185.     short level0 = 0;
  1186.     
  1187.     DisposeShapeAt(&layout->scrap);
  1188.     layout->scrap = GXNewLayout(
  1189.       1,
  1190.       &runLength,
  1191.       (void *) &text,
  1192.       1,
  1193.       &runLength,
  1194.       &defaultStyle,
  1195.       1,
  1196.       &runLength,
  1197.       &level0,
  1198.       nil,
  1199.       nil);
  1200.     
  1201.   }
  1202.   
  1203.   DisposHandle(buffer);
  1204.   
  1205.   UnlockEditHandle(handle);
  1206. }
  1207.  
  1208. void LayoutEditToScrap(LayoutEditHandle handle)
  1209. { LayoutEditPtr layout = LockEditHandle(handle);
  1210.   
  1211.   ZeroScrap();
  1212.   
  1213.   if (layout->scrap)
  1214.   { long textLength;
  1215.     Ptr textPtr;
  1216.     Handle shapeHandle = ShapeToHandle(layout->scrap);
  1217.     
  1218.   
  1219.     textLength = GXGetLayout(layout->scrap, nil, nil, nil, nil, nil, nil, nil, nil, nil);
  1220.     textPtr = NewPtr(textLength);
  1221.     GXGetLayout(layout->scrap, (void *) textPtr, nil, nil, nil, nil, nil, nil, nil, nil);
  1222.     
  1223.     PutScrap(GetHandleSize(shapeHandle), 'FLAY', LockHandle(shapeHandle));
  1224.     PutScrap(textLength, 'TEXT', textPtr);
  1225.     
  1226.     DisposeShapeAt(&layout->scrap);
  1227.     DisposPtr(textPtr);
  1228.     DisposHandle(shapeHandle);
  1229.   }
  1230.   
  1231.   UnlockEditHandle(handle);
  1232. }
  1233.  
  1234.  
  1235. void DisposeLayoutEditHandle(LayoutEditHandle handle)
  1236. { LayoutEditPtr layout = (LayoutEditPtr) LockHandle(handle);
  1237.  
  1238.   GXDisposeShape(layout->layout);
  1239.   DisposeShapeAt(&layout->highlight);
  1240.   DisposeShapeAt(&layout->scrap);
  1241.   DisposeStyleAt(&layout->insertionStyle);
  1242.   DisposeSelection(layout->selection);
  1243.   DisposHandle(handle);
  1244. }
  1245.